"""
@File    :   service.py
@Time    :   2024/04/13 12:50:43
@Author  :   Qrj
@Email   :   923207736@qq.com
@description   :   字典数据相关业务逻辑
"""

from cashews import NOT_NONE
from sqlalchemy import and_, delete, func, insert, select, update
from sqlalchemy.ext.asyncio import AsyncSession

from src.exceptions.custom_async_validator import generate_async_validator_exception
from src.schemas.validate import (
    OrderBySearchValidateSchema,
    PaginationSearchValidateSchema,
)
from src.utils.cashews import CashewsCondition, data_cache

from .models import DictData
from .schemas.out import (
    DictDataCompleteOutSchema,
    DictDataSimpleOutSchema,
    ListDictDataCompleteOutSchema,
    ListDictDataSimpleOutSchema,
)
from .schemas.validate import (
    DictDataCreateValidateSchema,
    DictDataDeleteValidateSchema,
    DictDataListSearchValidateSchema,
    DictDataUpdateStatusValidateSchema,
    DictDataUpdateValidateSchema,
)


class DictDataService:
    """字典数据业务逻辑类"""

    @data_cache(
        ttl="30m",
        key="dict_data#id:{dict_data_id}",
        tags=["dict_data#id", "dict_data#id:{dict_data_id}"],
        condition=NOT_NONE,
    )
    @staticmethod
    async def get_dict_data_use_id(
        db_session: AsyncSession,
        dict_data_id: int,
        raise_not_exists_exception: bool = True,
        raise_disabled_exception: bool = True,
    ) -> DictDataCompleteOutSchema | None:
        """获取字典数据信息实例(主键ID)

        Args:
            db_session (AsyncSession): 数据库连接池
            dict_data_id (int): ID
            raise_not_exists_exception (bool, optional): 没找到的话是否直接报错
            raise_disable_exception (bool, optional): 禁用的话是否直接报错

        Returns:
            DictDataCompleteOutSchema | None: 字典数据信息
        """
        # 信息查询sql
        search_sql = select(
            *DictDataCompleteOutSchema.convert_to_sql_model_field(DictData)
        ).where(DictData.id == dict_data_id)
        async with db_session.begin():
            search_res = await db_session.execute(search_sql)
        result = search_res.mappings().first()
        if result is not None:
            result = DictDataCompleteOutSchema.model_validate(result)
            if (not result.status) and raise_disabled_exception:
                # 禁用则报错
                raise generate_async_validator_exception("id", "字典数据已被禁用")
            return result
        elif result is None and raise_not_exists_exception:
            # 未找到则报错
            raise generate_async_validator_exception("id", "字典数据不存在")
        else:
            return result

    @data_cache(
        ttl="30m",
        key="complete_dict_data_list:{search_schema}:{pagination_schema}:{order_schema}",
        tags=["complete_dict_data_list"],
        condition=CashewsCondition.condition_only_true_result,
    )
    @staticmethod
    async def list_search_complete_dict_data(
        db_session: AsyncSession,
        search_schema: DictDataListSearchValidateSchema,
        pagination_schema: PaginationSearchValidateSchema,
        order_schema: OrderBySearchValidateSchema,
        need_select_total: bool = True,
    ) -> tuple[list[DictDataCompleteOutSchema], int | None]:
        """完整信息的数据类型列表查询

        Args:
            db_session (AsyncSession): 数据库连接池
            search_schema (DictDataListSearchValidateSchema): 查询参数
            pagination_schema: 分页参数
            order_schema: 排序参数
            need_select_total: 是否需要查询总数

        Returns:
            tuple[list[DictDataCompleteOutSchema], int | None]: 查询结果
        """
        # 构建排序字段
        order_by_field_sql = order_schema.convert_to_order_by_sql(
            search_schema, DictData
        )
        # 构建查询sql
        list_search_sql = (
            select(*DictDataCompleteOutSchema.convert_to_sql_model_field(DictData))
            .where(and_(*search_schema.convert_to_where_sql(DictData)))
            .order_by(order_by_field_sql)
            .offset(pagination_schema.offset)
            .limit(pagination_schema.limit)
        )
        # 开始查询
        async with db_session.begin():
            list_search_res = await db_session.execute(list_search_sql)
        result = list_search_res.mappings().all()
        # 根据need_select_total参数来确定是否需要查询总数
        if need_select_total:
            total_sql = (
                select(func.count())
                .select_from(DictData)
                .where(and_(*search_schema.convert_to_where_sql(DictData)))
            )
            async with db_session.begin():
                total_res = await db_session.execute(total_sql)
            total = total_res.scalar()
        else:
            total = None
        # 统一转为pydantic列表模型
        return ListDictDataCompleteOutSchema.validate_python(result), total

    @data_cache(
        ttl="30m",
        key="simple_dict_data_list:{search_schema}:{order_schema}",
        tags=["simple_dict_data_list"],
        condition=CashewsCondition.condition_only_true_result,
    )
    @staticmethod
    async def list_search_simple_dict_data(
        db_session: AsyncSession,
        search_schema: DictDataListSearchValidateSchema,
        order_schema: OrderBySearchValidateSchema,
    ) -> list[DictDataSimpleOutSchema]:
        """简单信息的数据类型列表查询

        Args:
            db_session (AsyncSession): 数据库连接池
            search_schema (DictDataListSearchValidateSchema): 查询参数
            pagination_schema: 分页参数
            order_schema: 排序参数
            need_select_total: 是否需要查询总数

        Returns:
            tuple[list[DictDataSimpleOutSchema], int | None]: 查询结果
        """
        # 构建排序字段
        order_by_field_sql = order_schema.convert_to_order_by_sql(
            search_schema, DictData
        )
        # 构建查询sql
        list_search_sql = (
            select(*DictDataCompleteOutSchema.convert_to_sql_model_field(DictData))
            .where(and_(*search_schema.convert_to_where_sql(DictData)))
            .order_by(order_by_field_sql)
        )
        # 开始查询
        async with db_session.begin():
            list_search_res = await db_session.execute(list_search_sql)
        result = list_search_res.mappings().all()
        # 统一转为pydantic列表模型
        return ListDictDataSimpleOutSchema.validate_python(result)

    @staticmethod
    @data_cache.transaction()
    async def create_dict_data(
        db_session: AsyncSession,
        create_schema: DictDataCreateValidateSchema,
        creator_username: str = "",
    ) -> int:
        """创建字典数据

        Args:
            db_session (AsyncSession): 数据库连接池
            create_info (TenantCreateValidateSchema): 创建信息
            creator_username: 创建者用户名

        Returns:
            int: 创建的sql主键ID
        """
        # 检查字典类型是否存在
        await DictDataService.verify_dict_type_exists(
            db_session, create_schema.dict_type
        )
        # 构建用户的创建信息字典
        create_info_dict = create_schema.model_dump()
        # 创建sql
        create_sql = insert(DictData).values(
            creator_username=creator_username,
            **create_info_dict,
        )
        async with db_session.begin():
            # 创建
            create_res = await db_session.execute(create_sql)
            # 清空相关缓存标签
            await data_cache.delete_tags("complete_dict_data_list")
        return create_res.inserted_primary_key_rows[0][0]

    @staticmethod
    @data_cache.transaction()
    async def update_dict_data(
        db_session: AsyncSession,
        update_schema: DictDataUpdateValidateSchema,
        updater_username: str = "",
    ):
        """修改字典数据信息

        Args:
            db_session (AsyncSession): 数据库连接池
            update_schema (DictDataUpdateValidateSchema): 修改操作所需的数据
            updater_username (str, optional):修改者用户名
        """
        # 检查需修改的sql实例是否存在(禁用不报错、过期不报错)
        update_sql_obj = await DictDataService.get_dict_data_use_id(
            db_session, update_schema.id, raise_disabled_exception=False
        )
        # 构建修改所需的数据(排除未设置的值和id)
        update_schema_data = update_schema.model_dump(
            exclude=("id"), exclude_unset=True
        )
        if update_schema_data:
            # 修改sql
            update_sql = (
                update(DictData)
                .values(**update_schema_data, updater_username=updater_username)
                .where(DictData.id == update_schema.id)
            )
            # 修改操作
            async with db_session.begin():
                # 修改sql数据
                await db_session.execute(update_sql)
                # 清空相关缓存标签
                await data_cache.delete_tags(
                    f"dict_data#id:{update_sql_obj.id}",
                    "complete_dict_data_list",
                )

    @staticmethod
    @data_cache.transaction()
    async def update_dict_data_status(
        db_session: AsyncSession,
        update_schema: DictDataUpdateStatusValidateSchema,
        updater_username: str = "",
    ):
        """单独修改字典数据状态

        Args:
            db_session (AsyncSession): 数据库连接池
            update_schema (DictDataUpdateStatusValidateSchema): 修改操作所需的数据
            updater_username (str, optional):修改者用户名
        """
        # 检查需修改的sql实例是否存在(禁用不报错、过期不报错)
        update_sql_obj = await DictDataService.get_dict_data_use_id(
            db_session, update_schema.id, raise_disabled_exception=False
        )
        # 修改sql
        update_sql = (
            update(DictData)
            .values(updater_username=updater_username, status=update_schema.status)
            .where(DictData.id == update_schema.id)
        )
        # 修改操作
        async with db_session.begin():
            # 修改sql数据
            await db_session.execute(update_sql)
            # 清空相关缓存标签
            await data_cache.delete_tags(
                f"dict_data#id:{update_sql_obj.id}",
                "complete_dict_data_list",
            )

    @staticmethod
    @data_cache.transaction()
    async def delete_dict_data(
        db_session: AsyncSession,
        delete_schema: DictDataDeleteValidateSchema,
    ):
        """删除字典数据

        Args:
            db_session (AsyncSession): 数据库连接池
            delete_schema (DictDataDeleteValidateSchema): 删除操作所属的数据
        """
        # 查找需删除的字典数据
        # 构建查询sql
        select_sql = select(DictData.id).where(DictData.id.in_(delete_schema.ids))
        # 进行sql查询
        async with db_session.begin():
            select_res = await db_session.execute(select_sql)
        select_result = select_res.mappings().all()
        # 获取需要删除的字典数据id列表
        delete_id_list = [i["id"] for i in select_result]
        # 进行删除操作
        if select_result:
            # 删除字典数据本身的sql
            delete_sql = delete(DictData).where(DictData.id.in_(delete_id_list))
            # 删除操作
            async with db_session.begin():
                # 删除sql中的数据
                await db_session.execute(delete_sql)
                # 清空相关缓存标签
                await data_cache.delete_tags(
                    *[f"dict_data#id:{i}" for i in delete_id_list],
                    "complete_dict_data_list",
                )

    @staticmethod
    async def verify_dict_type_exists(db_session: AsyncSession, dict_type: str):
        """验证字典类型是否存在

        Args:
            db_session (AsyncSession): 数据库连接池
            dict_type (str): 字典类型
        """
        from src.business.admin.system.dict.type.service import DictTypeService

        await DictTypeService.get_dict_type_use_type(
            db_session, dict_type, raise_disabled_exception=False
        )
